#include <stdio.h>
#include <iostream>
#include <conio.h>

#include <vector>
#include <unordered_set>

#include <SmegTools\Debug\ProfilerManager.hpp>

// ---------------------------------------------------------------------------
void ProfileCast();
void TryInit();
void TryConstructorsCalls();
void TryFunctionCalls();
void TryFunctionInheritance();
void TryBinaryOp();
void TryIteratorEnd();
void TryReverseString();
void TryUnion();

int main() {
    //ProfileCast();
    //TryInit();
    //TryConstructorsCalls();
    //TryFunctionCalls();
    TryFunctionInheritance();
    //TryBinaryOp();
    //TryIteratorEnd();
    //TryReverseString();
    //TryUnion();

    std::cout << "TEST FINISHED (Press Any key to quit)" << std::endl;
    _getch();
    return 0;
}

// ---------------------------------------------------------------------------
class TestPointer {
    int a;

    virtual void Init() {
    }
};
class TestPointerChild : public TestPointer {
    int b;
};

void ProfileCast() {
    system("cls");
    PROFILER_CLEAR()
    TestPointer* ptest;
    std::unordered_set<TestPointer*> setPtr;
    
    setPtr.insert( new TestPointer );
    setPtr.insert( new TestPointer );
    setPtr.insert( new TestPointer );
    setPtr.insert( new TestPointer );

    ptest = new TestPointer;

    setPtr.insert( ptest );

    int pPtrVal = (int)ptest;
    TestPointer* testp = reinterpret_cast<TestPointer*>(pPtrVal);

    PROFILER_ADD( "REINTERPRET" );
    PROFILER_ADD( "REINT_C_CAST" );
    
    PROFILER_ADD( "DYN_CAST" );
    PROFILER_ADD( "STA_CAST" );
    PROFILER_ADD( "C_CAST" );
    
    PROFILER_START( "REINTERPRET" )
    for ( int i = 0; i < 1000000; ++i ) {
        reinterpret_cast<TestPointer*>(pPtrVal);
    }
    PROFILER_STOP( "REINTERPRET" )

    PROFILER_START( "REINT_C_CAST" );
    for ( int i = 0; i < 1000000; ++i ) {
        (TestPointer*)(pPtrVal);
    }
    PROFILER_STOP( "REINT_C_CAST" )

    std::vector<TestPointer*> vecPtr;
    
    for ( unsigned i = 0; i < 1000000; ++i ) {
        vecPtr.push_back( new TestPointerChild );
    }
    
    PROFILER_START( "DYN_CAST" );
    for ( unsigned i = 0; i < 1000000; ++i ) {
        dynamic_cast<TestPointerChild*>(vecPtr[i]);
    }
    PROFILER_STOP( "DYN_CAST" )
    
    PROFILER_START( "STA_CAST" );
    for ( unsigned i = 0; i < 1000000; ++i ) {
        static_cast<TestPointerChild*>(vecPtr[i]);
    }
    PROFILER_STOP( "STA_CAST" )
    
    PROFILER_START( "C_CAST" );
    for ( unsigned i = 0; i < 1000000; ++i ) {
        (TestPointerChild*)(vecPtr[i]);
    }
    PROFILER_STOP( "C_CAST" )

    PROFILER_DEBUG_RENDER( true, true )

    std::cout << std::endl << "Press Any key to continue" << std::endl;
    _getch();

    std::cout << std::endl << std::endl;
}

// ---------------------------------------------------------------------------
class A{
public:
    int a;
};
class B{
public:
    B() {}
    int a;
};
class C{
public:
    C() : a() {}
    int a;
};
class D{
public:
    ~D() {}
    int a;
};
class E{
public:
    E() : a() {}
    ~E() {}
    int a;
};

void TryInit() {
    system("cls");
    PROFILER_CLEAR()
    std::cout << "************************** TryInit" << std::endl;
    
    // -------------
    A* a1 = new A;
    A* a2 = new A();

    std::cout << "****** A" << std::endl;
    std::cout << "*** new A : " << a1->a << std::endl;
    std::cout << "*** new A() : " << a2->a  << std::endl;
    
    delete a1;
    delete a2;
    
    PROFILER_ADD( "A_default" );
    PROFILER_ADD( "A_zero" );
    
    PROFILER_START( "A_default" )
    for ( int i = 0; i < 1000000; ++i ) {
        A* pTest = new A;
        delete pTest;
    }
    PROFILER_STOP( "A_default" )
    
    PROFILER_START( "A_zero" )
    for ( int i = 0; i < 1000000; ++i ) {
        A* pTest = new A();
        delete pTest;
    }
    PROFILER_STOP( "A_zero" )
    
    // -------------
    B* b1 = new B;
    B* b2 = new B();

    std::cout << "****** B" << std::endl;
    std::cout << "*** new B : " << b1->a << std::endl;
    std::cout << "*** new B() : " << b2->a  << std::endl;
    
    delete b1;
    delete b2;
    
    PROFILER_ADD( "B_default" );
    PROFILER_ADD( "B_zero" );
    
    PROFILER_START( "B_default" )
    for ( int i = 0; i < 1000000; ++i ) {
        B* pTest = new B;
        delete pTest;
    }
    PROFILER_STOP( "B_default" )
    
    PROFILER_START( "B_zero" )
    for ( int i = 0; i < 1000000; ++i ) {
        B* pTest = new B();
        delete pTest;
    }
    PROFILER_STOP( "B_zero" )
    
    // -------------
    C* c1 = new C;
    C* c2 = new C();

    std::cout << "****** C" << std::endl;
    std::cout << "*** new C : " << c1->a << std::endl;
    std::cout << "*** new C() : " << c2->a  << std::endl;
    
    delete c1;
    delete c2;
    
    PROFILER_ADD( "C_default" );
    PROFILER_ADD( "C_zero" );
    
    PROFILER_START( "C_default" )
    for ( int i = 0; i < 1000000; ++i ) {
        C* pTest = new C;
        delete pTest;
    }
    PROFILER_STOP( "C_default" )
    
    PROFILER_START( "C_zero" )
    for ( int i = 0; i < 1000000; ++i ) {
        C* pTest = new C();
        delete pTest;
    }
    PROFILER_STOP( "C_zero" )
    
    // -------------
    D* d1 = new D;
    D* d2 = new D();

    std::cout << "****** D" << std::endl;
    std::cout << "*** new D : " << d1->a << std::endl;
    std::cout << "*** new D() : " << d2->a  << std::endl;
    
    delete d1;
    delete d2;
    
    PROFILER_ADD( "D_default" );
    PROFILER_ADD( "D_zero" );
    
    PROFILER_START( "D_default" )
    for ( int i = 0; i < 1000000; ++i ) {
        D* pTest = new D;
        delete pTest;
    }
    PROFILER_STOP( "D_default" )
    
    PROFILER_START( "D_zero" )
    for ( int i = 0; i < 1000000; ++i ) {
        D* pTest = new D();
        delete pTest;
    }
    PROFILER_STOP( "D_zero" )
    
    // -------------
    E* e1 = new E;
    E* e2 = new E();

    std::cout << "****** E" << std::endl;
    std::cout << "*** new E : " << e1->a << std::endl;
    std::cout << "*** new E() : " << e2->a << std::endl;
    
    delete e1;
    delete e2;
    
    PROFILER_ADD( "E_default" );
    PROFILER_ADD( "E_zero" );
    
    PROFILER_START( "E_default" )
    for ( int i = 0; i < 1000000; ++i ) {
        E* pTest = new E;
        delete pTest;
    }
    PROFILER_STOP( "E_default" )
    
    PROFILER_START( "E_zero" )
    for ( int i = 0; i < 1000000; ++i ) {
        E* pTest = new E();
        delete pTest;
    }
    PROFILER_STOP( "E_zero" )
        
    std::cout << std::endl << "Press Any key to render profiler" << std::endl;
    _getch();

    PROFILER_DEBUG_RENDER( true, true )

    std::cout << std::endl << "Press Any key to continue" << std::endl;
    _getch();

    std::cout << std::endl << std::endl;
}
    
// ---------------------------------------------------------------------------
class TestCopy {
public:
    int b;
    static int a;
    
    TestCopy() {
        ++a;
        std::cout << "Default constructor : " << a << std::endl;
    }

    TestCopy( const TestCopy& _copy ) {
        ++a;
        std::cout << "Copy constructor : " << a << std::endl;
    }

    ~TestCopy() {
        --a;
        std::cout << "Destructor : " << a << std::endl;
    }

    TestCopy& operator=( const TestCopy& _copy ) {
        b = _copy.b;
        std::cout << "Assign operator" << std::endl;

        return *this;
    }
};

int TestCopy::a = 0;

void Normalize( TestCopy& _t ) {
    _t.b = 0;
}

TestCopy NormalizedCopy( const TestCopy& _t ) {
    return TestCopy();
}

void TryConstructorsCalls() {
    system("cls");
    PROFILER_CLEAR()
    std::cout << "************************** TryConstructorsCalls" << std::endl;

    std::cout << "*** Creating t1" << std::endl;
    TestCopy t1;
    std::cout << "*** Creating t2" << std::endl;
    TestCopy t2( t1 );
    std::cout << "*** Creating t3" << std::endl;
    TestCopy t3;
    std::cout << "*** Creating t4" << std::endl;
    TestCopy t4;
    
    std::cout << "*** Assigning t3" << std::endl;
    t3 = t1;
    
    std::cout << "*** Assigning t4" << std::endl;
    t4 = TestCopy();
    
    std::cout << "*** Normalize t1" << std::endl;
    Normalize( t1 );
    std::cout << "*** NormalizedCopy t2 by =" << std::endl;
    t2 = NormalizedCopy( t1 );
    std::cout << "*** NormalizedCopy t5 by =" << std::endl;
    TestCopy t5 = NormalizedCopy( t1 );
    std::cout << "*** NormalizedCopy t6 by ()" << std::endl;
    TestCopy t6( NormalizedCopy( t1 ) );

    std::cout << std::endl << "Press Any key to continue" << std::endl;
    _getch();

    std::cout << std::endl << std::endl;
}

// ---------------------------------------------------------------------------
class FuncA {
public:
    void NonVirtualCall1() {
        std::cout << "NonVirtualCall1 in A" << std::endl;
    }
    void NonVirtualCall2() {
        std::cout << "NonVirtualCall2 in A" << std::endl;
    }
    void NonVirtualCall3() {
        std::cout << "NonVirtualCall3 in A" << std::endl;
    }
    void NonVirtualCall4() {
        std::cout << "NonVirtualCall4 in A" << std::endl;
    }
};
class FuncB : public FuncA {
public:
    void NonVirtualCall1() {
        std::cout << "NonVirtualCall1 in B" << std::endl;
    }
    void NonVirtualCall2() {
        std::cout << "NonVirtualCall1 in B" << std::endl;
        FuncA::NonVirtualCall2();
    }
    virtual void NonVirtualCall3() {
        std::cout << "NonVirtualCall3 in B" << std::endl;
    }
    virtual void NonVirtualCall4() {
        std::cout << "NonVirtualCall4 in B" << std::endl;
    }
};
class FuncC : public FuncB {
public:
    virtual void NonVirtualCall3() {
        std::cout << "NonVirtualCall3 in C" << std::endl;
    }
};

void TryFunctionCalls() {
    system("cls");
    PROFILER_CLEAR()
    std::cout << "************************** TryFunctionCalls" << std::endl;

    FuncA* pA;
    FuncB* pB;

    FuncC c;
    
    pA = &c;
    pB = &c;
    
    std::cout << "********** NonVirtualCall1 from pA" << std::endl;
    pA->NonVirtualCall1();
    std::cout << "********** NonVirtualCall2 from pA" << std::endl;
    pA->NonVirtualCall2();
    std::cout << "********** NonVirtualCall3 from pA" << std::endl;
    pA->NonVirtualCall3();
    std::cout << "********** NonVirtualCall4 from pA" << std::endl;
    pA->NonVirtualCall4();

    std::cout << "********** NonVirtualCall1 from pB" << std::endl;
    pB->NonVirtualCall1();
    std::cout << "********** NonVirtualCall2 from pB" << std::endl;
    pB->NonVirtualCall2();
    std::cout << "********** NonVirtualCall3 from pB" << std::endl;
    pB->NonVirtualCall3();
    std::cout << "********** NonVirtualCall4 from pB" << std::endl;
    pB->NonVirtualCall4();

    std::cout << std::endl << "Press Any key to continue" << std::endl;
    _getch();

    std::cout << std::endl << std::endl;
}

// ---------------------------------------------------------------------------
class AABB;
class Line;

class BoundingVolume {
public:
    void IntersectsUnknownBBPtr( const BoundingVolume* _bb );
    virtual void Setup() {  std::cout << "BoundingVolume Setup" << std::endl; }
    virtual void Intersects( const AABB& _aabb ) = 0;
    virtual void Intersects( const Line& _line ) = 0;
};
class AABB : public BoundingVolume {
public:
    virtual void Setup( float fTest = 1.0f ) {  std::cout << "AABB Setup " << fTest << std::endl; BoundingVolume::Setup(); }
    virtual void Intersects( const AABB& _aabb ) {
        std::cout << "AABB intersects with AABB" << std::endl;
    }
    virtual void Intersects( const Line& _line ) {
        std::cout << "AABB intersects with Line" << std::endl;
    }
};
class Line : public BoundingVolume {
private:
    virtual void Setup() {  std::cout << "Line Setup" << std::endl; BoundingVolume::Setup(); }
public:
    virtual void SetupLine() {  std::cout << "Line Special Setup" << std::endl; BoundingVolume::Setup(); }
    virtual void Intersects( const AABB& _aabb ) {
        std::cout << "Line intersects with AABB" << std::endl;
    }
    virtual void Intersects( const Line& _line ) {
        std::cout << "Line intersects with Line" << std::endl;
    }
};

void BoundingVolume::IntersectsUnknownBBPtr( const BoundingVolume* _bb ) {
    const AABB* aabb = dynamic_cast<const AABB*>( _bb );
    if (aabb) {
        Intersects( *aabb );
        return;
    }
    const Line* line = dynamic_cast<const Line*>( _bb );
    if (line) {
        Intersects( *line );
        return;
    }
    std::cout << "No conversion available for Intersects" << std::endl;
}

void TryFunctionInheritance() {
    system("cls");
    PROFILER_CLEAR()
    std::cout << "************************** TryFunctionInheritance" << std::endl;

    std::cout << "********* Creating aabb1, aabb2, line1 and line2" << std::endl;
    AABB aabb1, aabb2;
    Line line1, line2;
    
    std::cout << std::endl;

    std::cout << "********* Creating paabb2, pline2" << std::endl;
    BoundingVolume* paabb2 = &aabb2, 
        * pline2 = &line2;
    
    std::cout << std::endl;
    
    std::cout << "********* aabb1 intersects with aabb2" << std::endl;
    aabb1.Intersects( aabb2 );
    std::cout << "********* aabb1 intersects with paabb2" << std::endl;
    aabb1.IntersectsUnknownBBPtr( paabb2 );
    std::cout << "********* aabb1 intersects with line2" << std::endl;
    aabb1.Intersects( line2 );
    std::cout << "********* aabb1 intersects with pline2" << std::endl;
    aabb1.IntersectsUnknownBBPtr( pline2 );
    
    std::cout << std::endl;

    std::cout << "********* line1 intersects with aabb2" << std::endl;
    line1.Intersects( aabb2 );
    std::cout << "********* line1 intersects with paabb2" << std::endl;
    line1.IntersectsUnknownBBPtr( paabb2 );
    std::cout << "********* line1 intersects with line2" << std::endl;
    line1.Intersects( line2 );
    std::cout << "********* line1 intersects with pline2" << std::endl;
    line1.IntersectsUnknownBBPtr( pline2 );
    
    std::cout << std::endl;

    std::cout << "********* line1 setup" << std::endl;
    //line1.Setup(); INACCESSIBLE
    line1.SetupLine();
    std::cout << "********* aabb1 setup" << std::endl;
    aabb1.Setup();
    std::cout << "********* pline2 setup" << std::endl;
    pline2->Setup();
    std::cout << "********* paabb2 setup" << std::endl;
    paabb2->Setup();

    std::cout << std::endl << "Press Any key to continue" << std::endl;
    _getch();

    std::cout << std::endl << std::endl;
}

// ---------------------------------------------------------------------------
bool IsOn( int _iCur, int _iOn ) {
    return (_iCur & _iOn) != 0;
}
void On( int &_iCur, int _iOn ) {
    _iCur |= _iOn;
}
void Off( int &_iCur, int _iOff ) {
    _iCur ^= _iOff;
}

void TryBinaryOp() {
    system("cls");
    PROFILER_CLEAR()
    std::cout << "************************** TryBinaryOp" << std::endl;

    int iBehavior = 0;
    std::cout << "Base behavior : " << iBehavior << std::endl;
    On( iBehavior, 1 << 0 );
    std::cout << "On 1 << 0 : " << iBehavior << std::endl;
    On( iBehavior, 1 << 1 );
    std::cout << "On 1 << 1 : " << iBehavior << std::endl;
    On( iBehavior, 1 << 3 );
    std::cout << "On 1 << 3 : " << iBehavior << std::endl;
    std::cout << "IsOn 1 << 0 : " << IsOn( iBehavior, 1 << 0 ) << std::endl;
    std::cout << "IsOn 1 << 1 : " << IsOn( iBehavior, 1 << 1 ) << std::endl;
    std::cout << "IsOn 1 << 2 : " << IsOn( iBehavior, 1 << 2 ) << std::endl;
    std::cout << "IsOn 1 << 3 : " << IsOn( iBehavior, 1 << 3 ) << std::endl;
    Off( iBehavior, 1 << 1 );
    std::cout << "Off 1 << 1 : " << iBehavior << std::endl;
    std::cout << "IsOn 1 << 1 : " << IsOn( iBehavior, 1 << 1 ) << std::endl;

    std::cout << std::endl << "Press Any key to continue" << std::endl;
    _getch();

    std::cout << std::endl << std::endl;
}

// ---------------------------------------------------------------------------
void TryIteratorEnd() {
    system("cls");
    PROFILER_CLEAR()
    std::cout << "************************** TryIteratorEnd" << std::endl;

    std::vector<int> vec;
    vec.push_back( 1 );
    vec.push_back( 2 );
    vec.push_back( 3 );
    vec.push_back( 4 );
    vec.push_back( 5 );

    std::vector<int>::iterator rvec = vec.end();
    --rvec;

    for (rvec; rvec != vec.begin(); --rvec) {
        std::cout << "rvec : " << *rvec << std::endl;
    }

    std::cout << "Vec end - 1 : " << *(vec.end() - 1) << std::endl;

    std::cout << std::endl << "Press Any key to continue" << std::endl;
    _getch();

    std::cout << std::endl << std::endl;
}

// ---------------------------------------------------------------------------
void ReverseStringXOR( char* _Str, int _iLength ) {
    --_iLength;
    while (_iLength > 0) {
        // Triple XOR swap
        //
        // x = 1010
        // y = 0011
        //
        // x = x ^ y => 1010 ^ 0011 => x = 1001, y = 0011
        // y = x ^ y => 1001 ^ 0011 => x = 1001, y = 1010
        // x = x ^ y => 1001 ^ 1010 => x = 0011, y = 1010
        *_Str           = *_Str ^ _Str[_iLength];
        _Str[_iLength]  = *_Str ^ _Str[_iLength];
        *_Str           = *_Str ^ _Str[_iLength];

        ++_Str;
        _iLength -= 2;
    }
}

void ReverseString( char* _Str, int _iLength ) {
    char swap;

    --_iLength;
    while (_iLength > 0) {
        swap = *_Str;
        *_Str = _Str[_iLength];
        _Str[_iLength] = swap;

        ++_Str;
        _iLength -= 2;
    }
}

void TryReverseString() {
    system("cls");
    PROFILER_CLEAR()
    std::cout << "************************** TryReverseString" << std::endl;

    char Test[] = "Bonjour";

    std::cout << "Before " << Test << std::endl;
    ReverseString( Test, strlen( Test ) );
    std::cout << "After " << Test << std::endl;

    char TestXOR[] = "Bonjour XOR";

    std::cout << "Before XOR " << TestXOR << std::endl;
    ReverseStringXOR( TestXOR, strlen( TestXOR ) );
    std::cout << "After XOR " << TestXOR << std::endl;

    std::cout << std::endl << "Press Any key to continue" << std::endl;
    _getch();

    std::cout << std::endl << std::endl;
}

// ---------------------------------------------------------------------------
struct TestUnion1 {
    int     iUn1;
    float   fUn1;
};
struct TestUnion2 {
    float       fUn2;
    unsigned    uUn2;
};

union TestUnion {
    TestUnion1  un1;
    TestUnion2  un2;
};

void TryUnion() {
    system("cls");
    PROFILER_CLEAR()
    std::cout << "************************** TryUnion" << std::endl;

    TestUnion myUn;

    myUn.un1.iUn1 = -12;
    myUn.un2.fUn2 = 4.f;
    myUn.un2.uUn2 = 6;
    
    std::cout << "un1 iUn1 " << myUn.un1.iUn1 << std::endl;
    std::cout << "un1 fUn1 " << myUn.un1.fUn1 << std::endl;
    std::cout << "un2 fUn2 " << myUn.un2.fUn2 << std::endl;
    std::cout << "un2 uUn2 " << myUn.un2.uUn2 << std::endl;

    std::cout << std::endl << "Press Any key to continue" << std::endl;
    _getch();

    std::cout << std::endl << std::endl;
}